Multi-label prediction with Banknotes

In [1]:
%reload_ext autoreload
%autoreload 2
%matplotlib inline
In [2]:
import fastai
from fastai.vision import *
In [3]:
#For CPU only 
#fastai.torch_core.defaults.device = 'cpu'
#defaults.device= 'cpu'

Multiclassification

In [4]:
path = Path('/home/jupyter/.fastai/data/banknotes/')
path_imgs=path/'imgs'
path_imgs.mkdir(parents=True, exist_ok=True)
path_imgs
Out[4]:
PosixPath('/home/jupyter/.fastai/data/banknotes/imgs')
In [5]:
src = (ImageList.from_folder(path_imgs,recurse=True)
               .split_by_rand_pct(valid_pct=.2))

src
Out[5]:
ItemLists;

Train: ImageList (211 items)
Image (3, 428, 500),Image (3, 255, 500),Image (3, 500, 305),Image (3, 279, 500),Image (3, 500, 435)
Path: /home/jupyter/.fastai/data/banknotes/imgs;

Valid: ImageList (52 items)
Image (3, 210, 500),Image (3, 500, 497),Image (3, 475, 500),Image (3, 261, 500),Image (3, 259, 500)
Path: /home/jupyter/.fastai/data/banknotes/imgs;

Test: None
In [6]:
src.train.items[0]
Out[6]:
PosixPath('/home/jupyter/.fastai/data/banknotes/imgs/euro/5/00000IMG_00000_BURST20190730233112417_COVER.jpg')
In [24]:
#single class
func=lambda i: str(i.parent.relative_to(path_imgs) )
#multi class
func=lambda i: (i.parent.relative_to(path_imgs).parts )
func(src.train.items[0])
Out[24]:
('euro', '5')
In [25]:
ll = src.label_from_func(func); ll
#ll = src.label_from_folder(); ll
Out[25]:
LabelLists;

Train: LabelList (211 items)
x: ImageList
Image (3, 428, 500),Image (3, 255, 500),Image (3, 500, 305),Image (3, 279, 500),Image (3, 500, 435)
y: MultiCategoryList
euro;5,euro;5,euro;5,euro;5,euro;5
Path: /home/jupyter/.fastai/data/banknotes/imgs;

Valid: LabelList (52 items)
x: ImageList
Image (3, 210, 500),Image (3, 500, 497),Image (3, 475, 500),Image (3, 261, 500),Image (3, 259, 500)
y: MultiCategoryList
usd;100,usd;5,euro;100,euro;10,euro;500
Path: /home/jupyter/.fastai/data/banknotes/imgs;

Test: None
In [26]:
tfms = get_transforms(do_flip=True,flip_vert=True, 
                      max_rotate=90, 
                      max_zoom=1.5, 
                      max_lighting=0.5, 
                      max_warp=0.5)
In [27]:
#so its reproducible
#np.random.seed(42)
In [28]:
def get_data(size,bs):
    size=int(size)
    bs=int(bs)
    data = (ll.transform(tfms, size=size)
        .databunch(bs=bs) #for CPU only add ,num_workers=0
        .normalize(imagenet_stats))
    return data
size,bs=256/2,20
data=get_data(size,bs)
In [29]:
data.show_batch(rows=4, figsize=(12,9))
In [30]:
arch = models.resnet50
In [32]:
acc_02 = partial(accuracy_thresh, thresh=0.2)
f_score = partial(fbeta, thresh=0.2)
#multiclass
learn = cnn_learner(data, arch, metrics=[acc_02, f_score])
#single class
#learn = cnn_learner(data, arch, metrics=[accuracy])

We use the LR Finder to pick a good learning rate.

In [33]:
learn.lr_find()
learn.recorder.plot()
LR Finder is complete, type {learner_name}.recorder.plot() to see the graph.

Then we can fit the head of our network.

In [34]:
lr = 1e-2
In [35]:
learn.fit_one_cycle(10, slice(lr),callbacks=ShowGraph(learn))
epoch train_loss valid_loss accuracy_thresh fbeta time
0 0.750216 0.719378 0.336538 0.584017 00:03
1 0.663398 0.550227 0.530769 0.631121 00:02
2 0.543230 0.291684 0.844231 0.688161 00:02
3 0.451540 0.264833 0.873077 0.720086 00:02
4 0.387328 0.217006 0.869231 0.749514 00:02
5 0.340638 0.207760 0.890385 0.780594 00:02
6 0.307649 0.190863 0.892308 0.802642 00:02
7 0.278479 0.187269 0.888462 0.800019 00:02
8 0.256784 0.181053 0.888462 0.807692 00:02
9 0.239320 0.180444 0.894231 0.818765 00:02
In [36]:
learn.fit_one_cycle(5, slice(lr),callbacks=ShowGraph(learn))
epoch train_loss valid_loss accuracy_thresh fbeta time
0 0.166640 0.162642 0.911538 0.844309 00:02
1 0.178471 0.144832 0.923077 0.864510 00:02
2 0.169657 0.163556 0.913462 0.853827 00:02
3 0.169518 0.133805 0.930769 0.869561 00:02
4 0.167115 0.133194 0.934615 0.878982 00:02
In [37]:
learn.show_results(rows=3)
In [38]:
learn.save('stage-1-rn50')

...And fine-tune the whole model:

In [39]:
learn.unfreeze()
In [40]:
learn.lr_find()
learn.recorder.plot()
LR Finder is complete, type {learner_name}.recorder.plot() to see the graph.
In [41]:
lr=1e-4
learn.fit_one_cycle(5, slice(1e-5, lr/5),callbacks=ShowGraph(learn))
epoch train_loss valid_loss accuracy_thresh fbeta time
0 0.162866 0.135222 0.928846 0.867813 00:02
1 0.161705 0.128359 0.940385 0.883256 00:02
2 0.157154 0.126754 0.942308 0.892968 00:02
3 0.152936 0.124659 0.938462 0.881799 00:02
4 0.151399 0.122079 0.938462 0.902584 00:02
In [42]:
learn.save('stage-2-rn50')
In [44]:
learn.fit_one_cycle(5, slice(1e-5, lr/5),callbacks=ShowGraph(learn))
epoch train_loss valid_loss accuracy_thresh fbeta time
0 0.137417 0.120887 0.930769 0.876360 00:02
1 0.129040 0.116353 0.944231 0.893745 00:02
2 0.125685 0.110766 0.948077 0.902778 00:02
3 0.130170 0.110027 0.944231 0.900835 00:02
4 0.135834 0.114781 0.948077 0.896562 00:02
In [45]:
learn.show_results(rows=3)
In [63]:
gc.collect()
torch.cuda.empty_cache()
In [64]:
size,bs=256,10/4
data=get_data(size,bs)
In [65]:
learn.freeze()
In [66]:
learn.lr_find()
learn.recorder.plot()
LR Finder is complete, type {learner_name}.recorder.plot() to see the graph.
In [67]:
lr=5e-3
In [68]:
learn.fit_one_cycle(5, slice(lr),callbacks=ShowGraph(learn))
epoch train_loss valid_loss accuracy_thresh fbeta time
0 0.141479 0.119030 0.955769 0.909674 00:05
1 0.139201 0.109024 0.948077 0.916472 00:04
2 0.128050 0.098571 0.953846 0.927933 00:04
3 0.117926 0.093698 0.955769 0.923563 00:04
4 0.111025 0.089890 0.957692 0.931430 00:04
In [69]:
learn.fit_one_cycle(5, slice(lr),callbacks=ShowGraph(learn))
epoch train_loss valid_loss accuracy_thresh fbeta time
0 0.100608 0.087177 0.957692 0.938228 00:04
1 0.098751 0.095864 0.951923 0.904429 00:04
2 0.095786 0.083529 0.967308 0.925602 00:04
3 0.098323 0.076730 0.973077 0.930167 00:04
4 0.093342 0.073315 0.967308 0.925602 00:04
In [70]:
learn.save('stage-1-256-rn50')
In [71]:
learn.show_results()
In [72]:
learn.unfreeze()
In [73]:
learn.fit_one_cycle(5, slice(1e-5, lr/5),callbacks=ShowGraph(learn))
epoch train_loss valid_loss accuracy_thresh fbeta time
0 0.082577 0.072013 0.978846 0.954934 00:05
1 0.086123 0.085554 0.967308 0.918415 00:04
2 0.085773 0.068654 0.980769 0.950758 00:05
3 0.078720 0.060229 0.976923 0.954060 00:04
4 0.074564 0.059380 0.978846 0.955128 00:04
In [74]:
learn.save('stage-2-256-rn50')
In [75]:
learn.fit_one_cycle(5, slice(1e-5, lr/5),callbacks=ShowGraph(learn))
epoch train_loss valid_loss accuracy_thresh fbeta time
0 0.062882 0.051055 0.990385 0.971542 00:04
1 0.062569 0.049302 0.982692 0.952312 00:04
2 0.059774 0.055009 0.976923 0.947941 00:04
3 0.053390 0.054156 0.975000 0.944930 00:04
4 0.056220 0.052251 0.976923 0.946387 00:05
In [76]:
learn.save('stage-3-256-rn50')
In [77]:
learn.export()
In [ ]:
path
In [ ]:
# If using single class
In [21]:
interp = ClassificationInterpretation.from_learner(learn)
In [22]:
interp.plot_top_losses(9, figsize=(15,11),heatmap=True)
In [23]:
interp.plot_confusion_matrix()
In [ ]: